import { OpFunc, WhereOperators } from "./OpFunc";

/**
* name 
*/
export class HashMap<T>{
	private _values: Array<T>;
	private _indexHash: { [key: string]: number };
	protected _cls: any;		//有些地方需要class壳处理逻辑
	constructor(cls?: any) {
		this._values = [];
		this._indexHash = {};
		this._cls = cls;
	}

	protected _installT(d: T): T {
		if (this._cls) {
			var newD = new this._cls();
			lowCpData(newD, d);
			return newD;
		}
		return d;
	}


	/**
	 * 增加对象
	 * @param key 
	 * @param val 
	 * @return 增加返回true，更新返回false
	 */
	public add(key: string | number, val: T) {
		if (this._indexHash[key] == null) {
			val = this._installT(val);
			this._values.push(val);
			this._indexHash[key] = this._values.length - 1;
			return true
		} else {
			let index = this._indexHash[key];
			if (this._cls) {
				lowCpData(this._values[index], val);
			} else {
				this._values[index] = val;
			}
			return false;
		}
	}

	public get(key: string | number): T {
		let index = this._indexHash[key];
		return this._values[index];
	}

	public del(key: string | number): T {
		var index = this._indexHash[key];
		if (index == null) {
			return null
		}
		let value = this._values.splice(index, 1)[0];
		this._clearIndex(index);
		return value;
	}

	public getAt(index: number): T {
		return this._values[index];
	}

	public delAt(index: number): T {
		if (index >= this._values.length) return null;
		let value = this._values[index];
		this._values.splice(index, 1);
		this._clearIndex(index);
		return value;
	}

	private _clearIndex(index: number) {
		for (let key in this._indexHash) {
			let currIndex = this._indexHash[key];
			if (currIndex > index) {
				this._indexHash[key] -= 1;
			} else if (currIndex == index) {
				delete this._indexHash[key];
			}
		}
	}

	findOne(condition: WhereOption<T>) {
		for (var i = 0, len = this._values.length; i < len; i++) {
			if (this._checkCondition(this._values[i], condition)) return this._values[i];
		}
		return null;
	}

	find(condition: WhereOption<T>) {
		return this._values.filter((v) => {
			return this._checkCondition(v, condition);
		});
	}

	findOneByMethod(method: compareFunc<T>) {
		for (var i = 0, len = this._values.length; i < len; i++) {
			if (method(this._values[i])) return this._values[i];
		}
		return null;
	}

	findByMethod(method: compareFunc<T>) {
		return this._values.filter((v) => {
			return method(v);
		});
	}

	foreach(method: (d: T) => void) {
		for (let i = 0, len = this._values.length; i < len; i++) {
			method(this._values[i]);
		}
	}

	private _checkCondition(d: T, condition?: WhereOption<T>): boolean {
		for (var key in condition) {
			let child:any = condition[key];
			if (child instanceof Array) {
				if ((<Array<any>>child).indexOf(d[key]) == -1) return false;
			} else {
				let type = typeof child;
				if (type == "number" || type == "string" || type == "boolean") {
					if (child != d[key]) return false;
				} else {
					let ops = Object.getOwnPropertySymbols(child);
					for (let i = 0, len = ops.length; i < len; i++) {
						if (!OpFunc.inst.check(ops[i], d[key], child[ops[i]])) return false;
					}
				}
			};
		}
		return true;
	}


	public has(key: string | number): boolean {
		return this._indexHash[key] != null;
	}

	public get length(): number {
		return this._values.length;
	}

	public get values(): Array<T> {
		return this._values.concat();
	}


	public dispose() {
		this._values = null;
		this._indexHash = null;
	}
}

/**简单的浅层拷贝 chkTargetKey判断是否要考虑目标的key是否存在 */
export function lowCpData(target: any, data: any, chkTargetKey: boolean = false) {
	for (var key in data) {
		if (data.hasOwnProperty(key)) {
			var element = data[key];
			if (element == null) continue;
			if ((chkTargetKey && target.hasOwnProperty(key)) || !chkTargetKey) {
				target[key] = element;
			}
		}
	}
}

export type WhereOption<T> = { [P in keyof T]?: WhereValue };
export type WhereValue = number | string | boolean | Array<number | string | boolean> | WhereOperators;
export type compareFunc<T> = (item: T) => boolean;